home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / msysjour / vol06 / 05 / windos / winio.c < prev    next >
C/C++ Source or Header  |  1991-09-01  |  34KB  |  990 lines

  1. /*
  2. WINIO.C
  3. Stdio (e.g. printf) functionality for Windows - implementation
  4. Dave Maxey - 1991
  5. revisions by Andrew Schulman - 1991
  6. originally in Microsoft Systems Journal, July 1991
  7. revised for use by article in MSJ, September 1991
  8. */
  9.  
  10. #include <windows.h>
  11. #include <stdlib.h>
  12. #include <stdarg.h>
  13. #include <malloc.h>
  14. #include <string.h>
  15. #include "wmhandlr.h"
  16. #include "winio.h"
  17.  
  18. /* PROTOTYPES in alphabetic order */
  19.  
  20. void            addchars(BYTE *, unsigned);
  21. void            adjust_caret(void);
  22. void            append2buffer(BYTE *, unsigned);
  23. int             chInput(void);
  24. void            compute_repaint(void);
  25. int             initialize_buffers(unsigned);
  26. int             initialize_class(HANDLE);
  27. void            initialize_state(void);
  28. int             initialize_window(HANDLE, HANDLE, int);
  29. void            make_avail(unsigned);
  30. BYTE far *      nextline(BYTE far *);
  31. BYTE far *      prevline(BYTE far *);
  32. void            set_font(void);
  33. long            winio_wmpaint(HWND, unsigned, WORD, LONG);
  34. long            winio_wmsize(HWND, unsigned, WORD, LONG);
  35. long            winio_wmdestroy(HWND, unsigned, WORD, LONG);
  36. long            winio_wmchar(HWND, unsigned, WORD, LONG);
  37. long            winio_wmkeydown(HWND, unsigned, WORD, LONG);
  38. long            winio_wmhscroll(HWND, unsigned, WORD, LONG);
  39. long            winio_wmvscroll(HWND, unsigned, WORD, LONG);
  40. long            winio_wmsetfocus(HWND, unsigned, WORD, LONG);
  41. long            winio_wmkillfocus(HWND, unsigned, WORD, LONG);
  42.  
  43. // this doesn't get declared in stdio.h if _WINDOWS is defined
  44. // although it is in the Windows libraries!
  45. int             vsprintf(char *, const char *, va_list);
  46.  
  47. #define         winio_caret_visible() \
  48.                 ((yCurrLine <= (yTopOfWin + yWinHeight)) && \
  49.                 (xCurrPos <= (xLeftOfWin + xWinWidth)) && \
  50.                 (xCurrPos >= xLeftOfWin))
  51.  
  52. #define         CHECK_INIT() if (! tWinioVisible) return FALSE
  53.                  
  54. #define         MAX_X                   127
  55. #define         TABSIZE                 8
  56. #define         TYPE_AHEAD              256
  57. #define         WINIO_DEFAULT_BUFFER    8192
  58. #define         MIN_DISCARD             256
  59. #define         CARET_WIDTH             2
  60.  
  61. // For scrolling procedures
  62. #define         USE_PARAM               10000
  63. #define         DO_NOTHING              10001
  64.  
  65. BYTE            winio_class[15] = "winio_class";
  66. BYTE            winio_icon[15] = "winio_icon";
  67. BYTE            winio_title[128] = "Stdio Window";
  68. unsigned long   bufsize = WINIO_DEFAULT_BUFFER;
  69. unsigned long   kbsize = TYPE_AHEAD;
  70. unsigned        bufused, bufSOI;
  71. unsigned        curr_font = SYSTEM_FIXED_FONT;
  72. int             tWinioVisible = FALSE;
  73. int             tCaret = FALSE, tFirstTime = TRUE;
  74. int             cxChar, cyChar, cxScroll, cyScroll, cxWidth, cyHeight;
  75. int             xWinWidth, yWinHeight, xCurrPos;
  76. int             xLeftOfWin, yTopOfWin, yCurrLine;
  77. unsigned        pchKbIn, pchKbOut;
  78. static BYTE far *fpBuffer, far *fpTopOfWin, far *fpCurrLine; 
  79. static BYTE far *fpKeyboard;
  80. static HANDLE   hBuffer, hKeyboard;
  81. static HWND     hwnd;
  82. BOOL            tTerminate = TRUE,
  83.                 tPaint = TRUE,
  84.                 tEcho = TRUE;
  85. DESTROY_FUNC    destroy_func;
  86.  
  87. typedef struct {
  88.     int hSB, vSB;
  89.     } recVKtoSB;
  90.                 
  91. /* This table defines, by scroll message, what increment to try */
  92. /* and scroll horizontally. PGUP and PGDN entries are updated   */
  93. /* in the winio_wmsize function.                                */
  94. int             cScrollLR[SB_ENDSCROLL + 1] =
  95. //UP  DOWN PGUP     PGDN    POS        TRACK      TOP     BOT    ENDSCROLL
  96. { -1, +1,  -1,      +1,     USE_PARAM, USE_PARAM, -MAX_X, MAX_X, DO_NOTHING};
  97.                 
  98. /* This table defines, by scroll message, what increment to try */
  99. /* and scroll horizontally. PGUP and PGDN entries are updated   */
  100. /* in the winio_wmsize function, and the TOP & BOTTOM entries   */
  101. /* are updated by addchar function.                             */
  102. int             cScrollUD[SB_ENDSCROLL + 1] =
  103. //UP  DOWN PGUP     PGDN    POS        TRACK      TOP     BOT    ENDSCROLL
  104. { -1, +1,  -1,      +1,     USE_PARAM, USE_PARAM, -1,     +1,    DO_NOTHING};
  105.                 
  106. /* This table associates horizontal and vertical scroll         */
  107. /* messages that should be generated by the arrow and page keys */
  108. recVKtoSB       VKtoSB[VK_DOWN - VK_PRIOR + 1] =
  109. //                  VK_PRIOR                    VK_NEXT
  110.                 {   { DO_NOTHING, SB_PAGEUP },  { DO_NOTHING, SB_PAGEDOWN },
  111. //                  VK_END                      VK_HOME
  112.                     { SB_TOP, SB_BOTTOM },      { SB_TOP, SB_TOP },
  113. //                  VK_LEFT                     VK_UP
  114.                     { SB_LINEUP, DO_NOTHING },  { DO_NOTHING, SB_LINEUP },
  115. //                  VK_RIGHT                    VK_DOWN
  116.                     { SB_LINEDOWN, DO_NOTHING },{ DO_NOTHING, SB_LINEDOWN } };
  117.                 
  118. /* ===================================================================  */
  119. /* the interface functions themselves.....                              */
  120. /* ===================================================================  */
  121.  
  122. char *gets(char *pchTmp)
  123.     {
  124.     BYTE *pch = pchTmp;
  125.     int c;
  126.  
  127.     CHECK_INIT();
  128.     bufSOI = bufused; /* mark beginning of line to limit backspace */
  129.     do {
  130.         if ((c = fgetchar()) == '\n')
  131.             c = '\0';
  132.         switch (c)
  133.             {
  134.             case '\b' :     if (pch > pchTmp) pch--; break;
  135.             case 0x1b :     pch = pchTmp; break;
  136.             case EOF :      bufSOI = -1; return NULL;
  137.             default :       *pch = (BYTE) c; pch++;
  138.             }
  139.         } while (c);
  140.     bufSOI = -1;
  141.     return pchTmp;
  142.     }
  143.  
  144. int printf(const char *fmt, ...)
  145.     {
  146.     va_list marker;
  147.     va_start(marker, fmt);
  148.     return vprintf(fmt, marker);
  149.     }
  150.  
  151. int vprintf(const char *fmt, va_list marker)
  152.     {
  153.     static BYTE s[1024];
  154.     int len;
  155.  
  156.     CHECK_INIT();
  157.     len = vsprintf(s, fmt, marker);
  158.     addchars(s,len);
  159.     return len;
  160.     }
  161.  
  162. int fgetchar(void)
  163.     {
  164.     int ch;
  165.     CHECK_INIT();
  166.     ch = chInput();
  167.     if (tEcho) 
  168.         fputchar(ch);
  169.     return ch;
  170.     }
  171.  
  172. int kbhit(void)
  173.     {
  174.     CHECK_INIT();
  175.     return (pchKbIn == pchKbOut);
  176.     }
  177.  
  178. int fputchar(int c)
  179.     {
  180.     CHECK_INIT();
  181.     addchars(&((char) c), 1);
  182.     return c;
  183.     }
  184.  
  185. int puts(const char *s)
  186.     {
  187.     BYTE c = '\n';
  188.     CHECK_INIT();
  189.     addchars((BYTE *) s, strlen(s));
  190.     addchars(&c, 1);
  191.     return 0;
  192.     }
  193.  
  194. /* ---------------------------------------------------------------  */
  195. /* USED INTERNALLY - pops up an error window and returns FALSE      */
  196. /* ---------------------------------------------------------------  */
  197. int fail(BYTE *s)
  198.     {
  199.     MessageBox(NULL,s,"ERROR",MB_OK);
  200.     return FALSE;
  201.     }
  202.  
  203. /* ---------------------------------------------------------------  */
  204. /* pops up a message window                                         */
  205. /* ---------------------------------------------------------------  */
  206. BOOL winio_warn(BOOL confirm, const BYTE *fmt, ...)
  207.     {
  208.     BYTE s[256];
  209.     va_list marker;
  210.  
  211.     va_start(marker, fmt);
  212.     vsprintf(s, fmt, marker);
  213.     va_end(marker);
  214.     
  215.     return (MessageBox(NULL, s, winio_title, 
  216.         confirm? MB_OKCANCEL : MB_OK) == IDOK);
  217.     }
  218.  
  219. /* ---------------------------------------------------------------  */
  220. /* The application must call this function before using any of the  */
  221. /* covered stdio type calls. We need the parameter info in order    */
  222. /* to create the window. The function allocates the buffer and      */
  223. /* creates the window. It returns TRUE or FALSE.                    */
  224. /* ---------------------------------------------------------------  */
  225. int winio_init(HANDLE hInstance, HANDLE hPrevInstance,
  226.             int nCmdShow, unsigned wBufSize)
  227.     {
  228.     if (tWinioVisible)
  229.         return FALSE;
  230.     
  231.     if (! initialize_buffers(wBufSize))
  232.         return FALSE;
  233.  
  234.     initialize_state();
  235.     
  236.     if (! initialize_window(hInstance, hPrevInstance, nCmdShow))
  237.         return FALSE;
  238.     
  239.     tWinioVisible = TRUE;
  240.     
  241.     atexit(winio_end);  /* hook into exit chain */
  242.  
  243.     winio_yield();
  244.     return TRUE;
  245.     }
  246.  
  247. /* ---------------------------------------------------------------  */
  248. /* Clear the contents of the buffer.                                */
  249. /* ---------------------------------------------------------------  */
  250. void winio_clear(void)
  251.     {
  252.     _fmemset(fpBuffer,0,(int) bufsize - 1);
  253.     fpCurrLine = fpTopOfWin = fpBuffer;
  254.     *fpBuffer = '\0';
  255.     xCurrPos = 0;
  256.     yCurrLine = 0;
  257.     yTopOfWin = 0;
  258.     xLeftOfWin = 0;
  259.     bufused = 0;
  260.  
  261.     if (tWinioVisible)
  262.         {
  263.         SetScrollRange(hwnd, SB_VERT, 1, yCurrLine + 1, FALSE);
  264.         SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);
  265.         }
  266.     }
  267.  
  268. /* ---------------------------------------------------------------  */
  269. /* Return the window handle of the underlying Windows object.       */
  270. /* Can be used by an application to customize the WINIO window      */
  271. /* ---------------------------------------------------------------  */
  272. HWND winio_hwnd(void)
  273.     {
  274.     return hwnd;
  275.     }
  276.  
  277. /* ---------------------------------------------------------------  */
  278. /* This function is called by winio_init(). It initializes a number */
  279. /* of global variables, including the WM_ handler table.            */
  280. /* ---------------------------------------------------------------  */
  281. void initialize_state()
  282.     {
  283.     winio_clear();
  284.     destroy_func = 0;
  285.     
  286.     /* set up our message handlers */
  287.     wmhandler_init();
  288.     wmhandler_set(WM_PAINT,       winio_wmpaint);
  289.     wmhandler_set(WM_SIZE,        winio_wmsize);
  290.     wmhandler_set(WM_DESTROY,     winio_wmdestroy);
  291.     wmhandler_set(WM_CHAR,        winio_wmchar);
  292.     wmhandler_set(WM_HSCROLL,     winio_wmhscroll);
  293.     wmhandler_set(WM_VSCROLL,     winio_wmvscroll);
  294.     wmhandler_set(WM_SETFOCUS,    winio_wmsetfocus);
  295.     wmhandler_set(WM_KILLFOCUS,   winio_wmkillfocus);
  296.     wmhandler_set(WM_KEYDOWN,     winio_wmkeydown);
  297.     }
  298.  
  299. /* ---------------------------------------------------------------  */
  300. /* This function is called by winio_init(). It initializes our      */
  301. /* Windows class, and some global variables                         */
  302. /* ---------------------------------------------------------------  */
  303. int initialize_window(HANDLE hInst, HANDLE hPrev, int nCmdShow)
  304.     {
  305.     static RECT start;
  306.     int cx, cy, inc;
  307.  
  308.     cx = GetSystemMetrics(SM_CXSCREEN);
  309.     cy = GetSystemMetrics(SM_CYSCREEN);
  310.     inc = GetSystemMetrics(SM_CYCAPTION);
  311.     cxScroll = GetSystemMetrics(SM_CXVSCROLL);
  312.     cyScroll = GetSystemMetrics(SM_CYHSCROLL);
  313.  
  314.     if (hPrev)
  315.         {
  316.         // note: other WINIO apps are NOT other instances!
  317.         GetInstanceData(hPrev, (NPSTR) &start, sizeof(RECT));
  318.         start.top += inc;
  319.         start.left += inc;
  320.         if (start.top > (cy >> 2))
  321.             start.top = cy >> 3;
  322.         if (start.left > (cx >> 2))
  323.             start.left = cx >> 3;
  324.         }
  325.     else
  326.         {
  327.         if (! initialize_class(hInst)) 
  328.             return fail("Could not create class");
  329.  
  330.         start.left = cx >> 3;
  331.         start.right = 3 * (cx >> 2);
  332.         start.top = cy >> 3;
  333.         start.bottom = 3 * (cy >> 2);
  334.         }
  335.         
  336.     hwnd = CreateWindow(winio_class, winio_title,
  337.         WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
  338.         start.left, start.top, start.right, start.bottom,
  339.         NULL, NULL, hInst, NULL);
  340.     if (! hwnd)
  341.         return fail("Could not create window");
  342.  
  343.     set_font();
  344.     
  345.     ShowWindow(hwnd, nCmdShow);
  346.     UpdateWindow(hwnd);
  347.  
  348.     return TRUE;
  349.     }
  350.  
  351. /* -----------------------------------------------------------------------  */
  352. /* Initializes Window Class                                                 */
  353. /* -----------------------------------------------------------------------  */
  354. int initialize_class(HANDLE hInst)
  355.     {
  356.     WNDCLASS  wc;
  357.  
  358.     wc.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNCLIENT;
  359.     wc.lpfnWndProc = WndProc;
  360.     wc.cbClsExtra = 0;
  361.     wc.cbWndExtra = 0;
  362.     wc.hInstance = hInst;
  363.     wc.hIcon = LoadIcon(hInst, winio_icon);
  364.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  365.     wc.hbrBackground = GetStockObject(WHITE_BRUSH); 
  366.     wc.lpszMenuName = NULL;
  367.     wc.lpszClassName = winio_class;
  368.  
  369.     return RegisterClass(&wc);
  370.     }
  371.     
  372. /* -----------------------------------------------------------------------  */
  373. /* Uses GlobalAlloc() to allocate the display and keyboard buffers          */
  374. /* -----------------------------------------------------------------------  */
  375. int initialize_buffers(unsigned wBufSize)
  376.     {
  377.     if (wBufSize)
  378.         bufsize = max(wBufSize, 1024);
  379.  
  380.     if (! (hBuffer = GlobalAlloc(GMEM_MOVEABLE, bufsize)))
  381.         return fail("Could not allocate/nconsole I/O buffer");
  382.     
  383.     fpBuffer = GlobalLock(hBuffer); // keep locked; assume protected mode
  384.     
  385.     if (! (hKeyboard = GlobalAlloc(GMEM_MOVEABLE, kbsize)))
  386.         return fail("Could not allocate/type ahead buffer");
  387.         
  388.     fpKeyboard = GlobalLock(hKeyboard);
  389.  
  390.     *fpBuffer = '\0';
  391.     fpBuffer++;
  392.  
  393.     return TRUE;
  394.     }
  395.  
  396. /* -----------------------------------------------------------------------  */
  397. /* Undoes the work of the above. Allows an application to close the window  */
  398. /* Terminates the prog.                                                     */
  399. /* -----------------------------------------------------------------------  */
  400. void winio_end()
  401.     {
  402.     while (tWinioVisible)
  403.         winio_yield();
  404.     }
  405.  
  406. /* -------------------------------------------------------------------  */
  407. /* Closes the window by sending it a WM_DESTROY message. Note that it   */
  408. /* does not disable the _onclose defined function. So the user program  */
  409. /* handler will be triggered. Does NOT cause the app. to terminate.     */
  410. /* -------------------------------------------------------------------  */
  411. void winio_close()
  412.     {
  413.     tTerminate = FALSE;
  414.     DestroyWindow(hwnd);
  415.     tTerminate = TRUE;
  416.     }
  417.     
  418. /* -------------------------------------------------------------------  */
  419. /* processes any outstanding events waiting. These may be characters    */
  420. /* typed at the keyboard, WM_PAINT messages, etc. It is called          */
  421. /* internally but should also be used liberally by the application      */
  422. /* within loops.                                                        */
  423. /* -------------------------------------------------------------------  */
  424. void winio_yield()
  425.     {
  426.     MSG msg;
  427.     CHECK_INIT();
  428.     while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
  429.         {
  430.         TranslateMessage(&msg);
  431.         DispatchMessage(&msg);
  432.         }
  433.     }
  434.  
  435. /* -------------------------------------------------------------------  */
  436. /* Let the application install an exit routine, called back from        */
  437. /* winio_wmdestroy(). Deinstall by winio_onclose(NULL)                  */
  438. /* -------------------------------------------------------------------  */
  439. void winio_onclose(DESTROY_FUNC exitfunc)
  440.     {
  441.     destroy_func = exitfunc;
  442.     }
  443.  
  444. /* -------------------------------------------------------------------  */
  445. /* This function allows the font of the window to be modified, and may  */
  446. /* be used BEFORE winio_init. Currently, only SYSTEM_, ANSI_, and       */
  447. /* OEM_FIXED_FONTs are supported.                                       */
  448. /* -------------------------------------------------------------------  */
  449. BOOL winio_setfont(WORD wFont)
  450.     {
  451.     if ((wFont != SYSTEM_FIXED_FONT) &&
  452.         (wFont != ANSI_FIXED_FONT) &&
  453.         (wFont != OEM_FIXED_FONT))
  454.         return FALSE;
  455.     curr_font = wFont;
  456.     if (tWinioVisible)
  457.         {
  458.         set_font();
  459.         if (tPaint)
  460.             InvalidateRect(hwnd, NULL, TRUE);
  461.         }
  462.     return TRUE;
  463.     }
  464.  
  465. /* -------------------------------------------------------------------  */
  466. /* This function allows the title of the window to be modified, and may */
  467. /* be used BEFORE winio_init.                                           */
  468. /* -------------------------------------------------------------------  */
  469. void winio_settitle(BYTE *pchTitle)
  470.     {
  471.     strncpy(winio_title, pchTitle, 127);
  472.     winio_title[127] = '\0';
  473.     if (tWinioVisible)
  474.         SetWindowText(hwnd, winio_title);
  475.     }
  476.  
  477. /* -------------------------------------------------------------------  */
  478. /* This function allows the caller to specifiy immediate or deferred    */
  479. /* screen updates. The call may not be issued before winio_init().      */
  480. /* -------------------------------------------------------------------  */
  481. BOOL winio_setpaint(BOOL on)
  482.     {
  483.     BOOL ret = tPaint;
  484.     
  485.     CHECK_INIT();
  486.     if (tPaint = on)
  487.         InvalidateRect(hwnd, NULL, TRUE);
  488.     return ret;
  489.     }
  490.  
  491. /* -------------------------------------------------------------------  */
  492. /* This function changes the behavior of getchar(), whose default       */
  493. /* is to echo characters, unlike DOS. winio_setecho(FALSE) restores     */
  494. /* the non-echo DOS behavior.                                           */
  495. /* -------------------------------------------------------------------  */
  496. BOOL winio_setecho(BOOL flag)
  497.     {
  498.     BOOL ret = tEcho;
  499.     tEcho = flag;
  500.     return ret;
  501.     }
  502.  
  503. /* ---------------------------------------------------------------  */
  504. /* Our WM_PAINT handler. It sends the currrent 'in view' piece of   */
  505. /* the buffer to the window. Note that an embedded NULL character   */
  506. /* signifies an end of line, not '\n'.                              */
  507. /* ---------------------------------------------------------------  */
  508. long winio_wmpaint(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  509.     {
  510.     HDC hdc;
  511.     PAINTSTRUCT ps;
  512.     BYTE far *pchSOL = fpTopOfWin;
  513.     BYTE far *pchEOL;
  514.     int i, j, xStart;
  515.     int xLeft, xRight, yTop, yBottom;
  516.  
  517.     hdc = BeginPaint(hwnd, &ps);
  518.  
  519.     xLeft = (ps.rcPaint.left / cxChar) + xLeftOfWin;
  520.     xRight = (ps.rcPaint.right / cxChar) + xLeftOfWin;
  521.     yTop = ps.rcPaint.top / cyChar;
  522.     yBottom = ps.rcPaint.bottom / cyChar;
  523.     SelectObject(hdc, GetStockObject(curr_font));
  524.  
  525.     for (i = 0; i < yTop; i++)      // lines above repaint region
  526.         {
  527.         while (*pchSOL)
  528.             pchSOL++;
  529.         pchSOL++;
  530.         }
  531.  
  532.     if (i <= yCurrLine) // something needs repainting..
  533.         {
  534.         for (i = yTop; i <= yBottom; i++)   // lines in repaint region
  535.             {
  536.             for (j = 0; (j < xLeft) && (*pchSOL); j++, pchSOL++)
  537.                 ; // Scroll right
  538.             pchEOL = pchSOL;
  539.             xStart = j - xLeftOfWin;
  540.             for (j = 0; (*pchEOL) ; j++, pchEOL++) ; // end of line
  541.             TextOut(hdc, cxChar * xStart, cyChar * i, pchSOL,
  542.                     min(j, xRight - xLeft + 2));
  543.             if ((unsigned)(pchEOL - fpBuffer) >= bufused)
  544.                 break;
  545.             pchSOL = ++pchEOL;  
  546.             }
  547.         }
  548.     
  549.     EndPaint(hwnd, &ps);
  550.     adjust_caret();
  551.     return 0;
  552.     }
  553.  
  554. /* ---------------------------------------------------------------  */
  555. /* Our WM_SIZE handler. It updates the internal record of our       */
  556. /* window size, minus the scroll bars, and recalcs the scroll bar   */
  557. /* ranges.                                                          */
  558. /* ---------------------------------------------------------------  */
  559. long winio_wmsize(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  560.     {
  561.     cxWidth = LOWORD(lParam);
  562.     cyHeight = HIWORD(lParam);
  563.  
  564.     xWinWidth   = (cxWidth - cxScroll ) / cxChar;
  565.     yWinHeight  = (cyHeight - cyScroll ) / cyChar;
  566.  
  567.     cScrollLR[SB_PAGEUP]    = -xWinWidth / 2;
  568.     cScrollLR[SB_PAGEDOWN]  = +xWinWidth / 2;
  569.     cScrollUD[SB_PAGEUP]    = -yWinHeight + 1;
  570.     cScrollUD[SB_PAGEDOWN]  = +yWinHeight - 1;
  571.     
  572.     SetScrollRange(hwnd, SB_HORZ, 1, MAX_X, FALSE);
  573.     SetScrollPos(hwnd, SB_HORZ, xLeftOfWin + 1, TRUE);
  574.  
  575.     SetScrollRange(hwnd, SB_VERT, 1, yCurrLine + 1, FALSE);
  576.     SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);
  577.     
  578.     return 0;
  579.     }
  580.  
  581. /* ---------------------------------------------------------------  */
  582. /* Our WM_DESTROY handler. It frees up storage associated with the  */
  583. /* window, and resets its state to uninitialized.                   */
  584. /* ---------------------------------------------------------------  */
  585. long winio_wmdestroy(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  586.     {
  587.     if (destroy_func)
  588.         (*destroy_func)();
  589.     GlobalUnlock(hBuffer);
  590.     GlobalUnlock(hKeyboard);
  591.     GlobalFree(hBuffer);
  592.     GlobalFree(hKeyboard);
  593.     tWinioVisible = FALSE;
  594.     if (tTerminate) exit(0);
  595.     return 0;
  596.     }
  597.  
  598. /* --------------------------------------------------------------- */
  599. /* Our WM_BYTE handler. It adds the BYTE to the internal kb buffer */
  600. /* if there is room otherwise it queues a BEEP                     */
  601. /* --------------------------------------------------------------- */
  602. long winio_wmchar(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  603.     {
  604.     BYTE far *lpchKeybd = fpKeyboard;
  605.     unsigned pchSave = pchKbIn;
  606.     
  607.     pchKbIn++;
  608.     if (pchKbIn == TYPE_AHEAD)
  609.         pchKbIn = 0;
  610.     if (pchKbIn == pchKbOut)
  611.         {
  612.         MessageBeep(0);
  613.         pchKbIn = pchSave;
  614.         }
  615.     else
  616.         *(lpchKeybd + pchSave) = LOBYTE(wParam);
  617.  
  618.     return 0;
  619.     }
  620.  
  621. /* ---------------------------------------------------------------  */
  622. /* Our WM_KEYDOWN handler. This handles what would be called        */
  623. /* function keys in the DOS world. In this case the function keys   */
  624. /* operate as scroll bar controls, so we generate messages to the   */
  625. /* scroll message handlers below.                                   */
  626. /* ---------------------------------------------------------------  */
  627. long winio_wmkeydown(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  628.     {
  629.     int hSB, vSB;
  630.     
  631.     if ((wParam < VK_PRIOR) || (wParam > VK_DOWN))
  632.         return 0;
  633.     
  634.     hSB = VKtoSB[wParam - VK_PRIOR].hSB;
  635.     vSB = VKtoSB[wParam - VK_PRIOR].vSB;
  636.     if (hSB != DO_NOTHING)
  637.         SendMessage(hwnd, WM_HSCROLL, hSB, 0L);
  638.     if (vSB != DO_NOTHING)
  639.         SendMessage(hwnd, WM_VSCROLL, vSB, 0L);
  640.     return 0;
  641.     }
  642.  
  643. /* --------------------------------------------------------------- */
  644. /* Our WM_HSCROLL handler. It adjusts what part of the buffer      */
  645. /* is visible. It operates as left/right arrow keys.               */
  646. /* --------------------------------------------------------------- */
  647. long winio_wmhscroll(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  648.     {
  649.     int cxSave = xLeftOfWin,
  650.         xInc = cScrollLR[wParam];
  651.     
  652.     if (xInc == DO_NOTHING)
  653.         return 0;
  654.     else if (xInc == USE_PARAM)
  655.         xLeftOfWin = LOWORD(lParam) - 1;
  656.     else
  657.         xLeftOfWin += xInc;
  658.     
  659.     if ((xLeftOfWin = max(0, min(MAX_X - 1, xLeftOfWin))) == cxSave)
  660.         return 0;
  661.  
  662.     ScrollWindow(hwnd, (cxSave - xLeftOfWin) * cxChar, 0, NULL, NULL);
  663.     SetScrollPos(hwnd, SB_HORZ, xLeftOfWin + 1, TRUE);
  664.     UpdateWindow(hwnd);
  665.  
  666.     return 0;
  667.     }
  668.  
  669. /* --------------------------------------------------------------- */
  670. /* Our WM_VSCROLL handler. It adjusts what part of the buffer      */
  671. /* is visible. It operates as page and line up/down keys.          */
  672. /* --------------------------------------------------------------- */
  673. long winio_wmvscroll(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  674.     {
  675.     int cySave = yTopOfWin,
  676.         yInc = cScrollUD[wParam],
  677.         i;
  678.     
  679.     if (yInc == DO_NOTHING)
  680.         return 0;
  681.     else if (yInc == USE_PARAM)
  682.         yTopOfWin = LOWORD(lParam) - 1;
  683.     else
  684.         yTopOfWin += yInc;
  685.  
  686.     if ((yTopOfWin = max(0, min(yCurrLine, yTopOfWin))) == cySave)
  687.         return 0;
  688.  
  689.     if (yTopOfWin > cySave)
  690.         for (i = cySave; i < yTopOfWin; i++)
  691.             fpTopOfWin = nextline(fpTopOfWin);
  692.     else
  693.         for (i = cySave; i > yTopOfWin; i--)
  694.             fpTopOfWin = prevline(fpTopOfWin);
  695.         
  696.     ScrollWindow(hwnd, 0, (cySave - yTopOfWin) * cyChar, NULL, NULL);
  697.     SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);
  698.     UpdateWindow(hwnd);
  699.  
  700.     return 0;
  701.     }
  702.  
  703. /* ---------------------------------------------------------------  */
  704. /* Our WM_SETFOCUS handler. It sets up the text caret.              */
  705. /* ---------------------------------------------------------------  */
  706. long winio_wmsetfocus(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  707.     {
  708.     CreateCaret(hwnd, NULL, CARET_WIDTH, cyChar);
  709.     
  710.     if ((tCaret = winio_caret_visible()))
  711.         {
  712.         SetCaretPos((xCurrPos - xLeftOfWin) * cxChar,
  713.                     (yCurrLine - yTopOfWin) * cyChar);
  714.         ShowCaret(hwnd);
  715.         }
  716.  
  717.     return 0;
  718.     }
  719.  
  720. /* ---------------------------------------------------------------  */
  721. /* Our WM_KILLFOCUS handler. It destroys the text caret.            */
  722. /* ---------------------------------------------------------------  */
  723. long winio_wmkillfocus(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  724.     {
  725.     if (tCaret)
  726.         {
  727.         HideCaret(hwnd);
  728.         tCaret = FALSE;
  729.         }
  730.     DestroyCaret();
  731.     return 0;
  732.     }
  733.  
  734. void set_font(void)
  735.     {
  736.     HDC hdc;
  737.     TEXTMETRIC tm;
  738.         
  739.     hdc = GetDC(hwnd);
  740.     SelectObject(hdc, GetStockObject(curr_font));
  741.     GetTextMetrics(hdc,&tm);
  742.     ReleaseDC(hwnd,hdc);
  743.     cxChar = tm.tmAveCharWidth;
  744.     cyChar = tm.tmHeight+tm.tmExternalLeading;
  745.     xWinWidth   = (cxWidth - cxScroll ) / cxChar;
  746.     yWinHeight  = (cyHeight - cyScroll ) / cyChar;
  747.     }
  748.  
  749. /* ---------------------------------------------------------------  */
  750. /* Adjusts the position of the caret, and shows or hides it, as     */
  751. /* appropriate.                                                     */
  752. /* ---------------------------------------------------------------  */
  753. void adjust_caret()
  754.     {
  755.     int t = winio_caret_visible();
  756.  
  757.     if (t)
  758.         SetCaretPos((xCurrPos - xLeftOfWin) * cxChar,
  759.                     (yCurrLine - yTopOfWin) * cyChar);
  760.     if (t && (! tCaret))
  761.         ShowCaret(hwnd);
  762.     if ((! t) && tCaret)
  763.         HideCaret(hwnd);
  764.     tCaret = t;
  765.     }
  766.  
  767. /* ---------------------------------------------------------------  */
  768. /* Computes, on the basis of what has just been updated, what area  */
  769. /* of the window needs to be repainted.                             */
  770. /* ---------------------------------------------------------------  */
  771. void compute_repaint(void)
  772.     {
  773.     RECT rc;
  774.     static int xCP = 0, yCL = 0;
  775.     int tWholeWin = FALSE;
  776.     
  777.     if (yCurrLine > (yTopOfWin + yWinHeight))
  778.         {
  779.         yTopOfWin = 0;
  780.         fpTopOfWin = fpBuffer;
  781.         while (yTopOfWin < (yCurrLine - ((yWinHeight + 1) / 2)))
  782.             {
  783.             fpTopOfWin = nextline(fpTopOfWin);
  784.             yTopOfWin++;
  785.             }
  786.         tWholeWin = TRUE;
  787.         }
  788.  
  789.     if ((xCurrPos < xLeftOfWin) || (xCurrPos > (xLeftOfWin + xWinWidth)))
  790.         {
  791.         xLeftOfWin = 0;
  792.         while (xLeftOfWin < (xCurrPos - ((xWinWidth + 1) / 2)))
  793.             xLeftOfWin++;
  794.         tWholeWin = TRUE;
  795.         }
  796.  
  797.     if (tWholeWin)
  798.         InvalidateRect(hwnd, NULL, TRUE);
  799.     else
  800.         {
  801.         rc.left = ((yCL == yCurrLine) ?
  802.             (min(xCP, xCurrPos) - xLeftOfWin) * cxChar : 0);
  803.         rc.top = (yCL - yTopOfWin) * cyChar;
  804.         rc.right = (xWinWidth + 1) * cxChar;
  805.         rc.bottom = (yCurrLine - yTopOfWin + 1) * cyChar;
  806.         InvalidateRect(hwnd, &rc, TRUE);
  807.         }
  808.     
  809.     yCL = yCurrLine;
  810.     xCP = xCurrPos;
  811.     }
  812.  
  813. /* ---------------------------------------------------------------  */
  814. /* Adds the supplied cch-long string to the display buffer, and     */
  815. /* ensures any changed part of the window is repainted.             */
  816. /* ---------------------------------------------------------------  */
  817. void addchars(BYTE *pch, unsigned cch)
  818.     {
  819.     int ycSave = yCurrLine;
  820.     int ytSave = yTopOfWin;
  821.     int xSave = xLeftOfWin;
  822.  
  823.     make_avail(cch);
  824.  
  825.     append2buffer(pch, cch);
  826.  
  827.     if (ycSave != yCurrLine)
  828.         SetScrollRange(hwnd, SB_VERT, 1, yCurrLine + 1, FALSE);
  829.  
  830.     if (! tPaint)
  831.         return;
  832.     
  833.     compute_repaint();
  834.  
  835.     cScrollUD[SB_TOP]       = -yCurrLine;
  836.     cScrollUD[SB_BOTTOM]    = yCurrLine;
  837.     if (ytSave != yTopOfWin)
  838.         SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);       
  839.  
  840.     if (xSave != xLeftOfWin)
  841.         SetScrollPos(hwnd, SB_HORZ, xLeftOfWin + 1, TRUE);
  842.  
  843.     winio_yield();
  844.     }
  845.  
  846. /* ---------------------------------------------------------------  */
  847. /* Add chars onto the display buffer, wrapping at end of line,      */
  848. /* expanding tabs, etc.                                             */
  849. /* ---------------------------------------------------------------  */
  850. void append2buffer(BYTE *pch, unsigned cch)
  851.     {
  852.     unsigned i;
  853.     
  854.     for (i = 0; i < cch; i++, pch++)
  855.         {
  856.         switch (*pch)
  857.             {
  858.             case '\n' :
  859.                 *pch = '\0';
  860.                 *(fpBuffer + bufused) = '\0';
  861.                 bufused++;
  862.                 fpCurrLine = fpBuffer + bufused;
  863.                 yCurrLine++;
  864.                 xCurrPos = 0;
  865.                 bufSOI = bufused;
  866.                 break;
  867.             case '\t' :
  868.                 do  {
  869.                     *(fpBuffer + bufused) = ' ';
  870.                     bufused++;
  871.                     xCurrPos++;
  872.                     } while ((xCurrPos % TABSIZE) != 0);
  873.                 break;
  874.             case EOF :
  875.                 break;
  876.             case '\b' :
  877.                 if (bufused > bufSOI)
  878.                     {
  879.                     bufused--;
  880.                     xCurrPos--;
  881.                     }
  882.                 break;
  883.             case 0x1b :
  884.                 while (bufused > bufSOI)
  885.                     {
  886.                     bufused--;
  887.                     xCurrPos--;
  888.                     }
  889.                 break;
  890.             case 0x07 :
  891.                 MessageBeep(0);
  892.                 break;
  893.             default :
  894.                 if (*pch > 0x1a)
  895.                     {
  896.                     if (xCurrPos >= MAX_X)
  897.                         {
  898.                         *(fpBuffer + bufused) = '\0';
  899.                         bufused++;
  900.                         xCurrPos = 0;
  901.                         yCurrLine++;
  902.                         fpCurrLine = fpBuffer + bufused;
  903.                         }
  904.                     xCurrPos++;
  905.                     *(fpBuffer + bufused) = *pch;
  906.                     bufused++;
  907.                     }
  908.             }
  909.         }
  910.     
  911.     *(fpBuffer + bufused) = '\0'; // '\0' terminator after end of buffer
  912.     }
  913.  
  914. /* ---------------------------------------------------------------  */
  915. /* If we have run out of room in the display buffer, drop whole     */
  916. /* lines, and move the remaining buffer up.                         */
  917. /* ---------------------------------------------------------------  */
  918. void make_avail(unsigned cch)
  919.     {
  920.     unsigned cDiscard = 0;
  921.     BYTE far *fpTmp;
  922.     unsigned i;
  923.  
  924.     if ((unsigned long)(bufused + cch + TABSIZE) < bufsize)
  925.         return;
  926.  
  927.     fpTmp = fpBuffer;
  928.     cDiscard = ((max(MIN_DISCARD, cch + 1) + MIN_DISCARD - 1)
  929.         / MIN_DISCARD)      // this gives a whole number of
  930.         * MIN_DISCARD;      // our allocation units.
  931.     fpTmp += (LONG) cDiscard;
  932.     fpTmp = nextline(fpTmp);
  933.     cDiscard = fpTmp - fpBuffer; 
  934.     _fmemcpy(fpBuffer, fpTmp, bufused - cDiscard + 1);
  935.     bufused -= cDiscard;
  936.     if ((int) bufSOI != -1) bufSOI -= cDiscard;
  937.     fpTmp = fpBuffer + (LONG) bufused;
  938.     for (i = 0; i < cDiscard; i++) *fpTmp++ = '\0';
  939.     fpCurrLine = fpBuffer;
  940.     xCurrPos = yCurrLine = 0;
  941.     for (i = 0; i < bufused; i++)
  942.         {
  943.         if (*fpCurrLine)
  944.             xCurrPos++;
  945.         else
  946.             {
  947.             xCurrPos = 0;
  948.             yCurrLine++;
  949.             }
  950.         fpCurrLine++;
  951.         }
  952.     xLeftOfWin = yTopOfWin = -9999;
  953.     
  954.     InvalidateRect(hwnd, NULL, TRUE);
  955.     }
  956.  
  957.  
  958. /* -------------------------------------------------------------------  */
  959. /* These two routines find the beginning of the next, and previous      */
  960. /* lines relative to their input pointer                                */
  961. /* -------------------------------------------------------------------  */
  962.  
  963. BYTE far *nextline(BYTE far *p) { while (*p) p++; return ++p; }
  964. BYTE far *prevline(BYTE far *p) { p--; do p--; while (*p); return ++p; }
  965.  
  966. /* -------------------------------------------------------------------  */
  967. /* Waits for a character to appear in the keyboard buffer, yielding     */
  968. /* while nothing is available. Then inserts it into the buffer.         */
  969. /* -------------------------------------------------------------------  */
  970. int chInput(void)
  971.     {
  972.     BYTE far *lpchKeyBd;
  973.     BYTE c;
  974.     
  975.     CHECK_INIT();
  976.     while (pchKbIn == pchKbOut)
  977.         winio_yield();
  978.         
  979.     lpchKeyBd = fpKeyboard;
  980.     c = *(lpchKeyBd + pchKbOut);
  981.  
  982.     pchKbOut++;
  983.     if (pchKbOut == TYPE_AHEAD)
  984.         pchKbOut = 0;
  985.     
  986.     // Do CR/LF and EOF translation
  987.     return (c == 0x1a) ? EOF : (c == '\r') ? '\n' : c;
  988.     }
  989.  
  990.